/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "pit_of_saron.h"
#include "SpellScript.h"
#include "SpellAuras.h"
#include "Player.h"
#include "WorldSession.h"
#include "Opcodes.h"
#include "CreatureGroups.h"

enum Yells
{
	SAY_AGGRO						= 14,
	SAY_SLAY_1						= 15,
	SAY_DEATH						= 17,
	SAY_FORGE_1						= 18,
	SAY_FORGE_2						= 19,

	SAY_BOULDER_HIT					= 16,
	EMOTE_DEEP_FREEZE				= 23,
};

#define EMOTE_THROW_SARONITE		"%s hurls a massive saronite boulder at you!"

enum MiscData
{
	EQUIP_ID_SWORD					= 49345,
	EQUIP_ID_MACE					= 49344,
	GO_SARONITE_ROCK				= 196485,
};

Position const northForgePos = {722.5643f, -234.1615f, 527.182f, 2.16421f};
Position const southForgePos = {639.257f, -210.1198f, 529.015f, 0.523599f};

enum Spells
{
	SPELL_PERMAFROST				= 70326,
	SPELL_THROW_SARONITE			= 68788,
	SPELL_THUNDERING_STOMP			= 68771,

	SPELL_CHILLING_WAVE				= 68778,
	SPELL_DEEP_FREEZE				= 70381,
};

#define SPELL_FORGE_BLADE			RAID_MODE(68774, 70334)
#define SPELL_FORGE_MACE			RAID_MODE(68785, 70335)
#define SPELL_SARONITE_TRIGGERED	RAID_MODE(68789, 70851)

enum Events
{
	EVENT_SPELL_THROW_SARONITE	= 1,
	EVENT_JUMP,
	EVENT_SPELL_CHILLING_WAVE,
	EVENT_SPELL_DEEP_FREEZE,
};

class boss_garfrost : public CreatureScript
{
public:
	boss_garfrost() : CreatureScript("boss_garfrost") { }

	struct boss_garfrostAI : public ScriptedAI
	{
		boss_garfrostAI(Creature* creature) : ScriptedAI(creature)
		{
			pInstance = creature->GetInstanceScript();
		}

		InstanceScript* pInstance;
		EventMap events;
		uint8 phase;
		bool bCanSayBoulderHit;

		void Reset()
		{
			me->RemoveAura(SPELL_PERMAFROST);
			SetEquipmentSlots(true);
			me->SetReactState(REACT_AGGRESSIVE);
			me->SetControlled(false, UNIT_STATE_ROOT);
			me->DisableRotate(false);
			phase = 0;
			bCanSayBoulderHit = true;

			events.Reset();
			if (pInstance)
				pInstance->SetData(DATA_GARFROST, NOT_STARTED);
		}

		void SetData(uint32 id, uint32 data)
		{
			if (id == 1 && pInstance)
				pInstance->SetData(DATA_ACHIEV_ELEVEN, 0);
		}

		void EnterCombat(Unit* /*who*/)
		{
			me->CastSpell(me, SPELL_PERMAFROST, true);

			Talk(SAY_AGGRO);
			DoZoneInCombat();
			events.RescheduleEvent(EVENT_SPELL_THROW_SARONITE, urand(5000,7500));

			if (pInstance)
				pInstance->SetData(DATA_GARFROST, IN_PROGRESS);
		}

		void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/)
		{
			if (phase == 0 && !HealthAbovePct(66) && !me->HasUnitState(UNIT_STATE_ROOT))
			{
				phase = 1;
				me->SetReactState(REACT_PASSIVE);
				me->SetTarget(0);
				me->SendMeleeAttackStop(me->GetVictim());
				events.DelayEvents(8000);
				me->CastSpell(me, SPELL_THUNDERING_STOMP, false);
				events.RescheduleEvent(EVENT_JUMP, 1250);
				return;
			}

			if (phase == 1 && !HealthAbovePct(33) && !me->HasUnitState(UNIT_STATE_ROOT))
			{
				events.CancelEvent(EVENT_SPELL_CHILLING_WAVE);
				phase = 2;
				me->SetReactState(REACT_PASSIVE);
				me->SetTarget(0);
				me->SendMeleeAttackStop(me->GetVictim());
				events.DelayEvents(8000);
				me->CastSpell(me, SPELL_THUNDERING_STOMP, false);
				events.RescheduleEvent(EVENT_JUMP, 1250);
				return;
			}
		}

		void MovementInform(uint32 type, uint32 id)
		{
			if (type != EFFECT_MOTION_TYPE || id != 0)
				return;

			if (phase == 1)
			{
				me->SetControlled(true, UNIT_STATE_ROOT);
				me->CastSpell(me, SPELL_FORGE_BLADE, false);
				Talk(SAY_FORGE_1);
			}
			else if (phase == 2)
			{
				me->SetControlled(true, UNIT_STATE_ROOT);
				me->RemoveAurasDueToSpell(SPELL_FORGE_BLADE);
				me->CastSpell(me, SPELL_FORGE_MACE, false);
				Talk(SAY_FORGE_2);
			}
		}

        void SpellHitTarget(Unit* target, const SpellInfo* spell)
		{
			if (spell->Id == SPELL_SARONITE_TRIGGERED)
			{
				if (bCanSayBoulderHit)
				{
					bCanSayBoulderHit = false;
					Talk(SAY_BOULDER_HIT);
				}
			}
			if (spell->Id == SPELL_FORGE_BLADE)
			{
				events.RescheduleEvent(EVENT_SPELL_CHILLING_WAVE, 10000);
				SetEquipmentSlots(false, EQUIP_ID_SWORD);
				me->SetReactState(REACT_AGGRESSIVE);
				me->SetControlled(false, UNIT_STATE_ROOT);
				me->DisableRotate(false);
				if (me->GetVictim())
				{
					AttackStart(me->GetVictim());
					me->SetTarget(me->GetVictim()->GetGUID());
				}
			}
			else if (spell->Id == SPELL_FORGE_MACE)
			{
				events.RescheduleEvent(EVENT_SPELL_DEEP_FREEZE, 10000);
				SetEquipmentSlots(false, EQUIP_ID_MACE);
				me->SetReactState(REACT_AGGRESSIVE);
				me->SetControlled(false, UNIT_STATE_ROOT);
				me->DisableRotate(false);
				if (me->GetVictim())
				{
					AttackStart(me->GetVictim());
					me->SetTarget(me->GetVictim()->GetGUID());
				}
			}
		}

		void UpdateAI(uint32 diff)
		{
			if (!UpdateVictim())
				return;

			if (me->GetVictim())
			{
				float x,y,z;
				me->GetVictim()->GetPosition(x, y, z);
				if (x<600.0f || x>770.0f || y<-270.0f || y>-137.0f || z<514.0f || z>550.0f)
				{
					me->SetHealth(me->GetMaxHealth());
					EnterEvadeMode();
					if (CreatureGroup* f = me->GetFormation())
					{
						const CreatureGroup::CreatureGroupMemberType& m = f->GetMembers();
						for (CreatureGroup::CreatureGroupMemberType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
							if (itr->first->IsAlive() && itr->first->IsInCombat() && !itr->first->IsInEvadeMode() && itr->first->IsAIEnabled)
								itr->first->AI()->EnterEvadeMode();
					}
					return;
				}
			}

			events.Update(diff);

			if (me->HasUnitState(UNIT_STATE_CASTING))
				return;

			switch(events.GetEvent())
			{
				case 0:
					break;
				case EVENT_SPELL_THROW_SARONITE:
					bCanSayBoulderHit = true;
					if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 140.0f, true))
					{
						WorldPacket data;
						ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_BOSS_EMOTE, LANG_UNIVERSAL, me, NULL, EMOTE_THROW_SARONITE);
						target->ToPlayer()->GetSession()->SendPacket(&data);
						me->CastSpell(target, SPELL_THROW_SARONITE, false);
					}
					events.RepeatEvent(urand(12500,20000));
					break;
				case EVENT_JUMP:
					me->DisableRotate(true);
					if (phase == 1)
						me->GetMotionMaster()->MoveJump(northForgePos.GetPositionX(), northForgePos.GetPositionY(), northForgePos.GetPositionZ(), 25.0f, 15.0f, 0);
					else if (phase == 2)
						me->GetMotionMaster()->MoveJump(southForgePos.GetPositionX(), southForgePos.GetPositionY(), southForgePos.GetPositionZ(), 25.0f, 15.0f, 0);
					events.PopEvent();
					break;
				case EVENT_SPELL_CHILLING_WAVE:
					me->CastSpell(me->GetVictim(), SPELL_CHILLING_WAVE, false);
					events.RepeatEvent(35000);
					break;
				case EVENT_SPELL_DEEP_FREEZE:
					if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true))
					{
						Talk(EMOTE_DEEP_FREEZE, target);
						me->CastSpell(target, SPELL_DEEP_FREEZE, false);
					}
					events.RepeatEvent(35000);
					break;
			}

			DoMeleeAttackIfReady();
		}

		void JustDied(Unit* /*killer*/)
		{
			Talk(SAY_DEATH);
			if (pInstance)
				pInstance->SetData(DATA_GARFROST, DONE);
		}

		void KilledUnit(Unit* who)
		{
			if (who->GetTypeId() == TYPEID_PLAYER)
				Talk(SAY_SLAY_1);
		}

		void EnterEvadeMode()
		{
			me->SetControlled(false, UNIT_STATE_ROOT);
			me->DisableRotate(false);
			ScriptedAI::EnterEvadeMode();
		}
	};

	CreatureAI* GetAI(Creature* creature) const
	{
		return new boss_garfrostAI(creature);
	}
};

class spell_garfrost_permafrost : public SpellScriptLoader
{
public:
	spell_garfrost_permafrost() : SpellScriptLoader("spell_garfrost_permafrost") { }

	class spell_garfrost_permafrost_SpellScript : public SpellScript
	{
		PrepareSpellScript(spell_garfrost_permafrost_SpellScript);

		std::list<WorldObject*> targetList;

		void Unload()
		{
			targetList.clear();
		}

		void FilterTargets(std::list<WorldObject*>& targets)
		{
			if (Unit* caster = GetCaster())
			{
				std::list<GameObject*> blockList;
				caster->GetGameObjectListWithEntryInGrid(blockList, GO_SARONITE_ROCK, 100.0f);

				if (!blockList.empty())
				{
					for (std::list<WorldObject*>::iterator itrU = targets.begin(); itrU != targets.end(); ++itrU)
						if (WorldObject* target = (*itrU))
						{
							bool valid = true;
							if (!caster->IsWithinMeleeRange(target->ToUnit()))
								for (std::list<GameObject*>::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
									if (!(*itr)->IsInvisibleDueToDespawn())
										if ((*itr)->IsInBetween(caster, target, 4.0f))
										{
											valid = false;
											break;
										}
							if (valid)
							{
								if (Aura* aur = target->ToUnit()->GetAura(70336))
									if (aur->GetStackAmount() >= 10 && caster->GetTypeId() == TYPEID_UNIT)
										caster->ToCreature()->AI()->SetData(1, aur->GetStackAmount());
								targetList.push_back(*itrU);
							}
						}
				}
				else
				{
					targetList = targets;
					return;
				}
			}

			targets = targetList;
		}

		void FilterTargetsNext(std::list<WorldObject*>& targets)
		{
			targets = targetList;
		}

		void Register()
		{
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garfrost_permafrost_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENEMY);
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garfrost_permafrost_SpellScript::FilterTargetsNext, EFFECT_1, TARGET_UNIT_DEST_AREA_ENEMY);
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_garfrost_permafrost_SpellScript::FilterTargetsNext, EFFECT_2, TARGET_UNIT_DEST_AREA_ENEMY);
		}
	};

	SpellScript* GetSpellScript() const
	{
		return new spell_garfrost_permafrost_SpellScript();
	}
};

void AddSC_boss_garfrost()
{
	new boss_garfrost();

	new spell_garfrost_permafrost();
}
